package cheme.ProcessModel;

import java.beans.PropertyChangeEvent;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.gef.commands.CommandStack;

import cheme.ProcessModel.Errors.ChemeInvalidMapKeyError;
import cheme.ProcessModel.Interpreter.Interpreter;
import cheme.ProcessModel.Interpreter.ModelFactory;
import cheme.ProcessModel.ModelActions.PostupdateModelActionFactory;
import cheme.ProcessModel.ModelActions.PreupdateModelActionFactory;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.OverallTagComponent;

public class ModelEnv extends PropertyAwareObject
{
	private static Map<IModelBase, ModelEnv> modelToEnv;
	
	IModelBase modelBase = null;
	
	Interpreter interpreter = null;
		
	PreupdateModelActionFactory preActions = null;
	PostupdateModelActionFactory postActions = null;
	
	OverallTagComponentInitPolicy overallInit = null;
	
	private ModelEnv(IModelBase modelBase)
	{
		this.modelBase = modelBase;
	}
	
	public static ModelEnv getInstanceFor(IModelElement modelKey)
	{
		ModelEnv instance = null;
		IModelElement root = modelKey.getRoot();
		
		if(root instanceof IModelBase)
		{
			instance = getModelToEnvMap().get(root);
			
			if(null == instance)
			{
				instance = new ModelEnv((IModelBase)root);
				getModelToEnvMap().put((IModelBase)root, instance);
			}
		}
		else
		{
			throw new ChemeInvalidMapKeyError();
		}
		
		return instance;
	}
	
	private static Map<IModelBase, ModelEnv> getModelToEnvMap()
	{
		if(null == modelToEnv)
		{
			modelToEnv = new HashMap<IModelBase, ModelEnv>();
		}
		
		return modelToEnv;
	}
	
	public void hookModelActionFactories(CommandStack commandStack)
	{
		preActions = new PreupdateModelActionFactory();
		commandStack.addCommandStackEventListener(preActions);
		
		postActions = new PostupdateModelActionFactory();
		commandStack.addCommandStackEventListener(postActions);		
	}

	public void hookModelElement(IModelElement element)
	{
		if(null != element)
		{
			element.addPropertyChangeListener(this);
		}
	}

	public void unhookModelElement(IModelElement element)
	{
		if(null != element)
		{
			element.removePropertyChangeListener(this);
		}
	}

	protected OverallTagComponentInitPolicy getOverallInit()
	{
		if(overallInit == null)
		{
			overallInit = new OverallTagComponentInitPolicy();
			
		}

		return overallInit;
	}
	
	public void initializeOverallTagComponent(OverallTagComponent component)
	{
		getOverallInit().initialize(component);
	}

	public Interpreter getInterpreter()
	{
		if(interpreter == null)
		{
			interpreter = new Interpreter((IModelElement)modelBase);
		}

		return interpreter;
	}
	
	
	@Override
	protected void handleInterpretation(PropertyChangeEvent evt)
	{
		ChemePropertyChangeEvent chemeEvt = (ChemePropertyChangeEvent)evt;
		
		if(chemeEvt.includesEvent(IPropertyAware.P_CHILD) || chemeEvt.includesEvent(IPropertyAware.P_CHILDREN))
		{
			interpretChildChange(evt);
		}
		else
		{
			interpretElementChange(evt);
		}
		
		getInterpreter().
			updateFor((ChemePropertyChangeEvent)evt);
	}
	
	protected void interpretElementChange(PropertyChangeEvent evt)
	{
		if(evt.getNewValue() instanceof IModelElement)
		{
			getInterpreter().
				interpretTextChange(
					(IModelElement)evt.getNewValue());
		}
	}
	
	protected void interpretChildChange(PropertyChangeEvent evt)
	{		
		if(evt.getSource() instanceof IParent)
		{
			if(null != evt.getNewValue())
			{
				if(evt.getNewValue() instanceof IInterpretable)
				{
					getInterpreter().
						interpretChildAddition(
							(IModelElement)evt.getNewValue());
				}
			}
			else
			{
				if(evt.getOldValue() instanceof IInterpretable)
				{
					getInterpreter().
						interpretChildRemoval(
							(IModelElement)evt.getOldValue());
				}
			}
		}
	}

	public IModelElement createModelElement(String type, IModelElement parent)
	{
		return ModelFactory.createModelElement(type, parent);
	}
}
